################################################################################
# Ganga Project. http://cern.ch/ganga
#
# $Id: CloudApp.py,v 1.1 2008-07-17 16:40:57 moscicki Exp $
################################################################################

from Ganga.GPIDev.Adapters.IApplication import IApplication
from Ganga.GPIDev.Adapters.IPrepareApp import IPrepareApp
from Ganga.GPIDev.Adapters.IRuntimeHandler import IRuntimeHandler
from Ganga.GPIDev.Adapters.StandardJobConfig import StandardJobConfig
from Ganga.GPIDev.Schema import *

from Ganga.Utility.Config import getConfig

from Ganga.GPIDev.Lib.File import *
#from Ganga.GPIDev.Lib.File import File
#from Ganga.GPIDev.Lib.File import SharedDir
from Ganga.GPIDev.Lib.Registry.PrepRegistry import ShareRef
from Ganga.GPIDev.Base.Proxy import isType
from Ganga.Core import ApplicationConfigurationError

import os, shutil
from Ganga.Utility.files import expandfilename
shared_path = os.path.join(expandfilename(getConfig('Configuration')['gangadir']),'shared',getConfig('Configuration')['user'])

class CloudApp(IPrepareApp):
    """
    Basic Application for starting VM instances on a Cloud system
    """
    _schema = Schema(Version(1,0), {
        'vm_image'  : SimpleItem(preparable=1,defvalue='',typelist=['str','Ganga.GPIDev.Lib.File.File.File'],comparable=1,doc='name or path to the VM image.'), 
        'vm_arch'   : SimpleItem(defvalue='x86_64',copyable=1,doc='architectur of the VM image.'), 
        'vm_type'   : SimpleItem(defvalue='kvm'   ,copyable=1,doc='type of the VM image.'), 
        'vm_context': SimpleItem(defvalue={},typelist=['str'],copyable=1,doc='VM contextualization variables'), 
        'is_prepared' : SimpleItem(defvalue=None, strict_sequence=0, visitable=1, copyable=1, typelist=['type(None)','bool'],protected=0,comparable=1,doc='Location of shared resources. Presence of this attribute implies the application has been prepared.'),
        'hash': SimpleItem(defvalue=None, typelist=['type(None)', 'str'], hidden=1, doc='MD5 hash of the string representation of applications preparable attributes')
        } )
    _category = 'applications'
    _name = 'CloudApp'
    _exportmethods = ['prepare', 'unprepare']

    def __init__(self):
        super(CloudApp,self).__init__()

    def unprepare(self, force=False):
        """
        Revert an Executable() application back to it's unprepared state.
        """
        logger.debug('Running unprepare in Executable app')
        if self.is_prepared is not None:
            self.decrementShareCounter(self.is_prepared.name)
            self.is_prepared = None

    def prepare(self,force=False):
        """
        A method to place the application into a prepared state.
        """

        if (self.is_prepared is not None) and (force is not True):
            raise Exception('%s application has already been prepared. Use prepare(force=True) to prepare again.'%(self._name))

        ## in this step, it's expected that the vm_context is filled in accordingly.
        logger.info('Preparing %s application.'%(self._name))

        ## 2. set application status of is_prepared 
        self.is_prepared = True

        return 1

    def configure(self,masterappconfig):
        from Ganga.Core import ApplicationConfigurationError
        import os.path
       
        ## add validation steps on VM variables here and raise ApplicationConfigurationError on failures
        if not self.vm_arch:
            raise ApplicationConfigurationError(None,'vm_arch has to be set')
        if not self.vm_image: 
            raise ApplicationConfigurationError(None,'vm_image has to be set')
        if not self.vm_type:
            raise ApplicationConfigurationError(None,'vm_type has to be set')

        ## making key-value pairs into the returned appconfig
        appconfig = {}
        appconfig['vm_context'] =  {'VM_NAME':'$NAME'}

        ## adding more key-value pairs customized by app.vm_context
        appconfig['vm_context'].update( self.vm_context )
 
        return (None,appconfig)

class OpenNebulaHandler(IRuntimeHandler):
    def prepare(self,app,appconfig,appmasterconfig,jobmasterconfig):
        from Ganga.GPIDev.Adapters.StandardJobConfig import StandardJobConfig
        c = OpenNebulaJobConfig(appconfig['vm_context'])
        return c

class OpenNebulaJobConfig(StandardJobConfig):
    '''Extends the standard Job Configuration with additional attributes'''
   
    def __init__(self, vm_context=''):
        pairs = []
        for k,v in vm_context.items():
             pairs.append( '%s=%s' % (k,v) ) 
        self.vm_context='CONTEXT=[ %s ]' % ','.join( pairs )
        StandardJobConfig.__init__(self)

    def getVMContext(self):
        return self.vm_context 
    
### binding application with backend
from Ganga.GPIDev.Adapters.ApplicationRuntimeHandlers import allHandlers
allHandlers.add('CloudApp','OpenNebula', OpenNebulaHandler)
